/*
设计模式里工厂模式一共可以提炼成三类工厂：

简单工厂
工厂方法
抽象工厂
*/

package main

import "fmt"

func main() {
	test1()
	test2()
	test3()
}

/*
简单工厂的优点是，简单，缺点嘛，如果具体产品扩产，就必须修改工厂内部，增加Case，一旦产品过多就会导致简单工厂过于臃肿
Printer 简单工厂要返回的接口类型 属于抽象的产品
*/

type Printer interface {
	Print(name string) string
}

// CnPrinter 产品1号
type CnPrinter struct {
}

func (p *CnPrinter) Print(name string) string {
	return "你好 " + name
}

// EnPrinter 产品2号
type EnPrinter struct {
}

func (p *EnPrinter) Print(name string) string {
	return "hello " + name
}

// CreateNewPrinter 工厂
func CreateNewPrinter(lang string) Printer {
	switch lang {
	case "cn":
		return new(CnPrinter)
	case "en":
		return new(EnPrinter)
	default:
		return new(CnPrinter)
	}
}

func test1() {
	printer := CreateNewPrinter("en")
	fmt.Println(printer.Print("Bob"))
	printer = CreateNewPrinter("cn")
	fmt.Println(printer.Print("张三"))
}

/*
工厂方法模式，先建立一个工厂方法的接口
由具体的方法实例来决定生产什么类型的产品
避免多一种产品就加一个case
导致工厂方法的函数过于臃肿
*/

// OperatorFactory 是抽象出来的工厂方法的接口，有一个Create函数，代表实际生成产品(子类)的方法
type OperatorFactory interface {
	Create() MathOperator
}

// MathOperator 是实际产品(子类)的抽象接口
type MathOperator interface {
	SetOperandA(int)
	SetOperandB(int)
	ComputeResult() int
}

/*
BaseOperator 这里将所有子类共有的属性和方法抽取出来做成一个Base
其他子类在实现的的时候只需要利用组合的方式就可以包含这些属性和方法，只需要再实现自己特殊的法方即可
*/
type BaseOperator struct {
	operandA, operandB int
}

// SetOperandA 所有产品(子类)都有这个方法，所以提取出来只实现一次即可 代码复用
func (bo *BaseOperator) SetOperandA(a int) {
	bo.operandA = a
}

// SetOperandB 同上
func (bo *BaseOperator) SetOperandB(b int) {
	bo.operandB = b
}

// PlusOperatorFactory 实际的工厂方法，负责实现具体的产品(子类)
type PlusOperatorFactory struct{}

// Create   PlusOperatorFactory是实际的生产方法,对应对抽象工厂接口OperatorFactory Create方法的实现
func (pf *PlusOperatorFactory) Create() MathOperator {
	return &PlusOperator{
		BaseOperator: &BaseOperator{},
	}
}

// PlusOperator 实际的产品(子类)
type PlusOperator struct {
	*BaseOperator
}

// ComputeResult 实际的产品需要实现自身特殊的方法和属性
func (po *PlusOperator) ComputeResult() int {
	return po.operandB + po.operandA
}

// MultiOperatorFactory 同上，新添加的子类只需实现抽象工厂接口和抽象产品接口
type MultiOperatorFactory struct {
}

// Create  抽象工厂接口的实现
func (mp *MultiOperatorFactory) Create() MathOperator {
	return &MultiOperator{
		&BaseOperator{},
	}
}

// MultiOperator 抽象产品(子类)的实现
type MultiOperator struct {
	*BaseOperator
}

// ComputeResult 实现自身特殊的方法和属性
func (mp *MultiOperator) ComputeResult() int {
	return mp.operandA * mp.operandB
}

func test2() {
	var factory OperatorFactory

	factory = &PlusOperatorFactory{}
	var mathOp MathOperator

	mathOp = factory.Create()
	mathOp.SetOperandA(1)
	mathOp.SetOperandB(2)
	fmt.Println(mathOp.ComputeResult())

	factory = &MultiOperatorFactory{}
	var multiOp MathOperator

	multiOp = factory.Create()
	multiOp.SetOperandA(1)
	multiOp.SetOperandB(2)
	fmt.Println(multiOp.ComputeResult())
}

/*
工厂方法模式的优点

灵活性增强，对于新产品的创建，只需多写一个相应的工厂类。
典型的解耦框架。高层模块只需要知道产品的抽象类，无须关心其他实现类，满足迪米特法则、依赖倒置原则和里氏替换原则。
工厂方法模式的缺点

类的个数容易过多，增加复杂度。
增加了系统的抽象性和理解难度。
只能生产一种产品，此弊端可使用抽象工厂模式解决。
*/

/*
抽象工厂模式：用于创建一系列相关的或者相互依赖的对象。
*/

// AbstractFactory 工厂类的抽象接口
type AbstractFactory interface {
	CreateTelevision() ITelevision
	CreateAirConditioner() IAirConditioner
}

// ITelevision 产品(子类)的抽象接口
type ITelevision interface {
	Watch()
}

// IAirConditioner 产品(子类)的抽象接口
type IAirConditioner interface {
	SetTemperature(int)
}

// HuaWeiFactory 工厂类的实例
type HuaWeiFactory struct {
}

// CreateTelevision HuaWeiFactory实例需要实现抽象工厂类的方法
func (hw *HuaWeiFactory) CreateTelevision() ITelevision {
	return &HuaWeiTV{}
}

// CreateAirConditioner HuaWeiFactory实例需要实现抽象工厂类的方法
func (hw *HuaWeiFactory) CreateAirConditioner() IAirConditioner {
	return &HuaWeiAirConditioner{}
}

// HuaWeiTV 产品(子类)实例
type HuaWeiTV struct {
}

// Watch 子类需要实现产品接口的方法
func (hw *HuaWeiTV) Watch() {
	fmt.Println("I am watching HuaWeiTV")
}

// HuaWeiAirConditioner 产品(子类)实例
type HuaWeiAirConditioner struct {
}

// SetTemperature 子类需要实现产品接口的方法
func (wh *HuaWeiAirConditioner) SetTemperature(temp int) {
	fmt.Printf("set HuaWeiAirConditionor %d 'C \n", temp)
}

type XiaoMiFactory struct {
}

type XiaoMiTV struct {
}

func (xm *XiaoMiTV) Watch() {
	fmt.Println("i am watching XiaoMiTV")
}

func (xm *XiaoMiFactory) CreateTelevision() ITelevision {
	return &XiaoMiTV{}
}

type XiaoMiAirConditioner struct {
}

func (xm *XiaoMiAirConditioner) SetTemperature(temp int) {
	fmt.Printf("set XiaoMiAirConditionor %d 'C \n", temp)
}

func (xm *XiaoMiFactory) CreateAirConditioner() IAirConditioner {
	return &XiaoMiAirConditioner{}
}

type AppleFactory struct {
}

type AppleTv struct {
}

func (ap *AppleTv) Watch() {
	fmt.Println("i am watching apple tv")
}

func (ap *AppleFactory) CreateTelevision() ITelevision {
	return &AppleTv{}
}

type AppleAirConditioner struct {
}

func (ap *AppleAirConditioner) SetTemperature(temp int) {
	fmt.Printf("set AppleAirConditionor %d 'C \n", temp)
}

func (ap *AppleFactory) CreateAirConditioner() IAirConditioner {
	return &AppleAirConditioner{}
}

func test3() {
	var factory AbstractFactory
	var tv ITelevision
	var air IAirConditioner

	factory = &HuaWeiFactory{}
	tv = factory.CreateTelevision()
	tv.Watch()
	air = factory.CreateAirConditioner()
	air.SetTemperature(30)

	factory = &XiaoMiFactory{}
	tv = factory.CreateTelevision()
	tv.Watch()
	air = factory.CreateAirConditioner()
	air.SetTemperature(20)

	factory = &AppleFactory{}
	tv = factory.CreateTelevision()
	tv.Watch()
	air = factory.CreateAirConditioner()
	air.SetTemperature(15)
}

/*抽象工厂模式的优点

当需要产品族时，抽象工厂可以保证客户端始终只使用同一个产品的产品族。
抽象工厂增强了程序的可扩展性，对于新产品族的增加，只需实现一个新的具体工厂即可，不需要对已有代码进行修改，符合开闭原则。
抽象工厂模式的缺点

规定了所有可能被创建的产品集合，产品族中扩展新的产品困难，需要修改抽象工厂的接口。
增加了系统的抽象性和理解难度。

*/
